Navionics GPs track of the Sicily eolian island trip.
The xml file with the navionics tracks is parsed.
- Coordinates, time and speed are extracted.
- Speed is converted from m/s to knots
- Distance is calculated from thinned 1min segment data for the total distance and cumulative per day
- COG is calculated as consecutive bearings from a thinned 10min segment dataset
Code
# temp <- tempfile(fileext = ".zip")
# download.file("https://drive.google.com/drive/folders/1FlAM74hAPtFC3qIcZF5nQ03UK-WP3hZc&export=download",
# temp)
# out <- unzip(temp, exdir = tempdir())
# gpx_parsed <- htmlTreeParse(file = out[14], useInternalNodes = TRUE)
gpx_parsed <- htmlTreeParse(file = "Sicily2021Tracks", useInternalNodes = TRUE)
coords <- xpathSApply(doc = gpx_parsed, path = "//trkpt", fun = xmlAttrs)
elevation <- xpathSApply(doc = gpx_parsed, path = "//trkpt/ele", fun = xmlValue)
date_time <- xpathSApply(doc = gpx_parsed, path = "//trkpt/time", fun = xmlValue)
speed <- xpathSApply(doc = gpx_parsed, path = "//trkpt/extensions/navionics_speed", fun = xmlValue)
df <- data.frame(
lat = as.numeric(coords["lat", ]),
lon = as.numeric(coords["lon", ]),
elevation = as.numeric(elevation),
date = lubridate::as_date(date_time),
time = lubridate::parse_date_time(date_time, "Y-m-d*H:M:S"),
kts = as.numeric(speed) * 1.944) |> # convert from m/s to knots
mutate(
dist_segment = track_distance(lat, lon) / 1852, # convert from meters to nm
)
# outlier check
outlier <- which(df$dist_segment > .5)
outlier_area <- c(outlier, outlier-1) |> sort()
df_1min <- df |>
dplyr::filter(second(time) < 1) |>
mutate(cog = track_bearing(lat, lon),
cog = ifelse(cog < 0, cog + 360, cog)
) |>
mutate(
dist_segment = track_distance(lat, lon) / 1852, # convert from meters to nm
)
df_1min$dist_segment[1] <- 0
df_1min <- df_1min |>
mutate(dist = cumsum(dist_segment) |> round(digits = 1)) |>
group_by(date) |>
mutate(dist_day = cumsum(dist_segment) |> round(digits = 1)) |>
ungroup()
df_10min <- df_1min |>
dplyr::filter(second(time) <1, minute(time) %% 10 == 0) |>
mutate(cog = track_bearing(lat, lon),
cog = ifelse(cog < 0, cog + 360, cog))
example lines
Code
lat lon elevation date time kts
1 38.15578 14.77282 48 2021-10-03 2021-10-03 08:16:54 2.60496
2 38.15582 14.77245 44 2021-10-03 2021-10-03 08:16:55 4.49064
3 38.15591 14.77257 57 2021-10-03 2021-10-03 08:16:55 4.49064
4 38.15587 14.77253 46 2021-10-03 2021-10-03 08:16:56 1.94400
5 38.15592 14.77252 50 2021-10-03 2021-10-03 08:16:58 2.43000
dist_segment
1 NA
2 0.022317834
3 0.008898046
4 0.003125085
5 0.002768580
Code
lat lon elevation date time kts
230251 38.15554 14.7731 44 2021-10-08 2021-10-08 15:24:03 0
230252 38.15554 14.7731 44 2021-10-08 2021-10-08 15:24:04 0
230253 38.15554 14.7731 45 2021-10-08 2021-10-08 15:24:05 0
230254 38.15554 14.7731 44 2021-10-08 2021-10-08 15:24:06 0
230255 38.15554 14.7731 44 2021-10-08 2021-10-08 15:24:07 0
dist_segment
230251 2.400859e-04
230252 5.974434e-05
230253 1.194887e-04
230254 5.974434e-05
230255 0.000000e+00
Extract full hour data.
Code
df_1hour <- df_10min |> mutate(hour = hour(time)) |>
group_by(date, hour) |>
slice(1) |>
ungroup() |> arrange(time)
#dplyr::filter(minute(time) == 0, second(time) < 1)
Trip overview
- Blue markers are the positions at the full hour for approximate logbook entries.
- Red markers show beginning and end of missing segments.
Code
leaflet() |>
addTiles() |>
addPolylines(data = df_1min, lat = ~lat, lng = ~lon,
color = "#000000", opacity = 0.8, weight = 3) |>
leaflet::addAwesomeMarkers(
data = df_1hour, lat = ~lat, lng = ~lon, label = ~as.character(hour(time))) |>
leaflet::addAwesomeMarkers(
data = df[outlier_area, ], lat = ~lat, lng = ~lon,
label = ~as.character(hour(time)),
icon = awesomeIcons(icon = 'ios-close', iconColor = 'black',
library = 'ion', markerColor = 'red'))
Code
leaflet() |>
addTiles() |>
addPolylines(data = df, lat = ~lat, lng = ~lon,
color = "#000000", opacity = 0.8, weight = 3) |>
leaflet::addAwesomeMarkers(
data = df_1hour, lat = ~lat, lng = ~lon, label = ~as.character(hour)) |>
leaflet::addAwesomeMarkers(
data = df[outlier_area, ], lat = ~lat, lng = ~lon,
label = ~as.character(hour(time)),
icon = awesomeIcons(icon = 'ios-close', iconColor = 'black',
library = 'ion', markerColor = 'red'))
Hourly log book
Code
df_1hour |>
mutate(latitude = deg2dms(lat, type = "cat"),
longitude = deg2dms(lon, type = "cat"),
# add distance
across(c(cog, kts, dist), \(x) round(x, 1))
) |>
mutate(time = sprintf("%02d:%02d", hour(time), minute(time)),
date = str_remove(as.character(date), "^[0-9]*-"))|>
select(date, time, latitude, longitude, cog, kts, dist_day, dist) |>
gt::gt() |>
gt::opt_interactive(use_text_wrapping = FALSE) |>
gt::cols_width(dplyr::any_of(c("latitude", "longitude")) ~ gt::px(150))
COG
Code
df_10min |>
ggplot(aes(x = time, y = cog)) + geom_point() + geom_line()
Code
df_1min |>
ggplot(aes(x = time, y = cog)) + geom_point() + geom_line()
Distance
Checking Cumulative Miles in total and per day. The full track shows missing segments, marked in red in the full data route above.
Code
df_10min |>
ggplot(aes(x = time, y = dist)) + geom_point() + geom_line()
Code
df_10min |>
ggplot(aes(x = time, y = dist_day)) + geom_point() + geom_line()
missing segments
Code
df |>
ggplot(aes(x = time, y = dist_segment)) + geom_point() + geom_line()